/*-----------------------------------------------------------------------------
 * stock_update.c - Stock update transmission related routines used by both 
 *                  server and clients.
 *
 * March 2010, Forhad Ahmed
 *-----------------------------------------------------------------------------
 */

#include "common/include/stock_common.h"
#include "common/include/stock_update.h"


void 
stock_update_dump(stock_update *upd)
{
    printf("Update: ");
    printf("%8s @ %6s [%s] [%s]\n", 
           upd->ticker, 
           stock_price_string(upd->data.price), 
           stock_time_string(upd->data.timestamp),
           inet_ntoa(upd->client));
}

static int 
stock_update_format(stock_update *upd, stock_update_msg *msg) 
{
    memset(msg->header, 0xFF, STOCK_HEADER_LEN);
    memcpy(msg->update.ticker, upd->ticker, STOCK_TICKER_LEN);
    msg->update.data.timestamp = htonl(upd->data.timestamp);
    msg->update.data.price = htonl(upd->data.price);
    msg->update.data.change = htonl(upd->data.change);

    return sizeof(stock_update_msg);
}


int 
stock_update_send(int sock, struct sockaddr_in *addr, stock_update *upd) 
{
    struct sockaddr_in *server;
    socklen_t           server_len = sizeof(struct sockaddr_in);
    char                server_buf[STOCK_UPDATE_MSG_SIZE];
    stock_update_msg   *msg;
    const char         *pmsg;
    int                 msgsize;

    server = addr;

    memset(server_buf, 0, STOCK_UPDATE_MSG_SIZE);
    msg = (stock_update_msg *) server_buf; 
    msgsize = stock_update_format(upd, msg);

    pmsg = (const char *) msg;

    if (sendto(sock, pmsg, msgsize, 0, 
              (struct sockaddr*) server, server_len) == -1) {
        fprintf(stderr, "sendto() failed to %s\n", inet_ntoa(server->sin_addr));
        return -1;
    }

    return 0;
}


static int
stock_update_parse(stock_update_msg *msg, stock_update *upd)
{
    if (stock_verify_header(msg) != 0) {
        warning("Update from %s has malformed header", inet_ntoa(upd->client));
        return -1;
    }

    memcpy(upd->ticker, msg->update.ticker, STOCK_TICKER_LEN);
    upd->data.timestamp = ntohl(msg->update.data.timestamp);
    upd->data.price = ntohl(msg->update.data.price);
    upd->data.change = ntohl(msg->update.data.change);

    return 0;
}


int 
stock_update_recv(int sock, stock_update *upd) 
{
    struct sockaddr_in client;
    socklen_t          client_len;
    char               client_buf[STOCK_UPDATE_MSG_SIZE];
    stock_update_msg  *msg;
    int                rc;

    memset(upd, 0, sizeof(stock_update));
    memset(client_buf, 0, STOCK_UPDATE_MSG_SIZE);
    msg = (stock_update_msg *) client_buf; 

    rc = recvfrom(sock, msg, STOCK_UPDATE_MSG_SIZE, MSG_WAITALL, 
                 (struct sockaddr*) &client, &client_len);

    if (rc < 0) {
        return -1;
    } else if (rc == 0) {
        return STOCK_ECONNRESET;
    } else if (rc != STOCK_UPDATE_MSG_SIZE) {
        return STOCK_EMSGSIZE;
    }

    /*
     * The 'client' address field in the stock_update structure is used only in
     * the recieve path by the server to know what client an update came from
     */
    memcpy(&upd->client, &client.sin_addr, sizeof(struct in_addr)); 

    if (stock_update_parse(msg, upd) != 0) {
        return STOCK_EMALFORMED;
    }

    return 0;
}

